package tk.hongbo.mpen;


import android.content.Context;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by HongboZhao 2021/10/10.
 */
public class Sort {

    public static final String FILE_NAME_DATA = "data";
    public static final String FILE_NAME_RESULT = "result";
    public static final String FILE_CACHE_DIR = "items";
    ExecutorService executor = Executors.newCachedThreadPool();

    /**
     * 二进制大文件排序
     */
    public void largeFileSort(Context appContext) {
        try {
            //数据文件检查
            File dataFile = new File(appContext.getFilesDir(), FILE_NAME_DATA);
            if (!dataFile.exists()) {
                return;
            }
            //输出文件准备
            File resultFile = createResultFile(appContext);

            //分段文件夹准备
            File cacheFile = createCacheDir(appContext);

            //解析后统计数字个数
            int count = readAndItemWrite(dataFile, cacheFile);

            //文件的合并输出
            output(resultFile, cacheFile, count);

            //输出完成，清理临时缓存文件
            clearCache(appContext);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 合并分段文件，并排序输出
     */
    public void output(File resultFile, File cacheFile, int count) {
        DataOutputStream outStream = null;
        DataInputStream[] streams = null;
        try {
            //文件输出流
            outStream = new DataOutputStream(new FileOutputStream(resultFile, true));

            //循环从分段缓存中取出数字写入合并文件
            File[] files = cacheFile.listFiles();

            //当前比较数组
            int[] task = new int[files.length];

            //打开所有分段文件流
            streams = new DataInputStream[files.length];
            for (int fi = 0; fi < files.length; fi++) {
                streams[fi] = new DataInputStream(new FileInputStream(files[fi]));
                //读取第一个数字加入比较数组
                task[fi] = streams[fi].readInt();
            }

            //开始比较取数
            for (int i = 0; i < count; i++) {
                int th = -1;
                Integer sequence = 0;
                for (int j = 0; j < task.length; j++) {
                    if (th == -1 || (task[j] != -1 && task[j] < th)) {
                        th = task[j];
                        sequence = j;
                    }
                }
                outStream.writeInt(th);

                //为下一次做准备
                if (streams[sequence].available() > 0) {
                    task[sequence] = streams[sequence].readInt();
                } else {
                    task[sequence] = -1;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭所有分段数据流
            if (streams != null && streams.length > 0) {
                for (DataInputStream stream : streams) {
                    try {
                        stream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            //关闭合并文件流
            if (outStream != null) {
                try {
                    outStream.flush();
                    outStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 分段读取原始文件，转换成数字，并保存
     *
     * @param dataFile
     * @param cacheFile
     */
    public int readAndItemWrite(File dataFile, File cacheFile) {
        DataInputStream in = null;
        try {
            long size = (long) Math.sqrt(dataFile.length() / 4);
            long sizeq = dataFile.length() / 4 - size * size;
            //读取二进制文件
            int count = 0;
            in = new DataInputStream(new FileInputStream(dataFile));
            ArrayList<Integer> smallArray = new ArrayList();
            while (in.available() > 0) {
                //读取一段做转换
                smallArray.add(in.readInt());
                if ((cacheFile.listFiles().length == 0 && smallArray.size() == size + sizeq)
                        || (cacheFile.listFiles().length > 0 && smallArray.size() == size)) {
                    new Thread(() -> {
                        writeItemFile(cacheFile, String.valueOf(System.currentTimeMillis()), smallArray); //本段存入本地缓存
                    }).start();
                    count += smallArray.size();
                    //准备下一次
                    smallArray.clear();
                }
            }
            return count;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return 0;
    }

    /**
     * 把数字集合写入文件
     *
     * @param cacheFile
     * @param smallArray
     */
    public void writeItemFile(File cacheFile, String fileName, ArrayList<Integer> smallArray) {
        //排序保存
        Collections.sort(smallArray);
        File itemFile = new File(cacheFile.getAbsolutePath(), fileName);
        DataOutputStream fout = null;
        try {
            if (!itemFile.exists()) {
                itemFile.createNewFile();
            }
            fout = new DataOutputStream(new FileOutputStream(itemFile, true));
            for (int i = 0; i < smallArray.size(); i++) {
                fout.writeInt(smallArray.get(i));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fout != null) {
                try {
                    fout.flush();
                    fout.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 创建空缓存目录
     *
     * @return
     */
    public File createCacheDir(Context appContext) {
        File cacheFile = new File(appContext.getCacheDir(), FILE_CACHE_DIR);
        clearCache(appContext);
        cacheFile.mkdir();
        return cacheFile;
    }

    /**
     * 删除缓存目录
     *
     * @param appContext
     */
    public void clearCache(Context appContext) {
        File cacheFile = new File(appContext.getCacheDir(), FILE_CACHE_DIR);
        if (cacheFile.exists() && cacheFile.isDirectory()) {
            for (File file : cacheFile.listFiles()) {
                file.delete();
            }
        }
        cacheFile.delete();
    }

    /**
     * 创建空结果文件
     *
     * @param appContext
     * @return
     */
    public File createResultFile(Context appContext) {
        File outFile = new File(appContext.getFilesDir(), FILE_NAME_RESULT);
        //生成合并后输出文件
        if (outFile.exists()) {
            outFile.delete();
        }
        try {
            outFile.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return outFile;
    }
}
